<template>
    <div class="w-calender-container">
        <div class="w-top">
            <div class="w-chooseMonth">
                <slot name="left"></slot>
            </div>
            <div class="w-top-date">
                {{ year }}年{{ `${month > 9 ? month : '0' + month}` }}月
            </div>
            <div class="w-btn-wrap">
                <span @click="handleShowLastMonth">上一月</span>
                <span @click="handleShowToday">今天</span>
                <span @click="handleShowNextMonth">下一月</span>
                <span v-if="isShowClear" @click="handleClear">清除</span>
            </div>
        </div>
        <div class="w-date_wrap">
            <ul class="w-week">
                <li style="margin-left: 6%">一</li>
                <li>二</li>
                <li>三</li>
                <li>四</li>
                <li>五</li>
                <li>六</li>
                <li>日</li>
            </ul>
            <ul class="w-day">
                <li v-for="(item,index) in days "
                    :class="{'c-isCurMonth':item.isNextMonth||item.isLastMonth,'c-isCurToday':item.isCurToday,'c-isActive':item.isActive,'c-is-previous-date':item.isPreviousDate,'user-select-none':true}"
                    :key="index" @mousedown="startSelection(item)"
                    @mouseover="handleMouseover(item)" @mouseup="endSelection(item)">
                    <span style="user-select: none;pointer-events: none">
                        {{ item.day }}
                    </span>
                    <span v-if="item.isShowPoint" class="w-point"
                        style="user-select: none;pointer-events: none"></span>
                </li>
            </ul>
        </div>

    </div>
</template>
<script>

export default {
    name: 'calender',
    props: {
        //是否是多选
        isMultipleChoice: {
            type: Boolean,
            default: false,
        },
        //是否显示清除按钮
        isShowClear: {
            type: Boolean,
            default: true
        },
        //选择的月份
        Month: {
            type: String,
        },
        //需要加圆点的数据
        //数据格式为map { new Date(2022-02-11).getTime():true}: 用年月日的时间戳为key
        pointMap: {
            type: Object,
            default: () => {
                return {}
            }
        },
        //是否禁止选择以前的日期 默认为空表示没有限制 注：如果只有数字则 默认时间是当天时间
        //{1:['2022-02-11']}表示2022-02-11以前的日期不能选择；1：表示小于该时间的禁止选择，必填
        //{2:['2022-02-11']}表示2022-02-11以后的日期不能选择；2：表示大于该时间的禁止选择，必填
        //{3:['2022-02-11','2022-03-11']}表示2022-02-11到2022-03-11之间的日期可以选择；3：表示两个时间内的时间可以选择，必填
        //{4:['2022-02-11','2022-03-11']}表示2022-02-11到2022-03-11之外的日期可以选择；4：表示两个时间之外的可以选择，必填
        restrictDate: {
            type: Object,
            default: () => {
                return null
            }
        }
    },
    watch: {
        Month: {
            handler(val) {
                if (val) {
                    const date = val.split('-').map(Number);
                    this.year = date[0];
                    this.month = date[1]
                    this.days = [];
                    //选择月份后重新跟更新时间
                    this.dealDate();
                }
            },
            immediate: true
        },
        pointMap: {
            handler(val) {
                if (val) {
                    this.dealDate();
                }
            },
            immediate: true
        }
    },
    data() {
        return {
            year: '',//年
            month: '',//月
            days: [],//日期
            //是否已经选择了开始时间
            isChooseOne: false,
            endTime: null,
            startTime: null,
            monthValue: '',//选择的月份
            chooseDateList: {},//如果是单选 则把选择的保存
            isStart: false,
        }
    },
    methods: {

        startSelection(item) {
            //console.log("开始")
            this.isStart = true
            this.handleMouseover(item);
        },
        handleMouseover(item) {
            if (this.isStart) {
                //console.log(item.date)
                this.handleChooseDay(item);
            }
        },
        endSelection(item) {
            //console.log("结束")
            this.isStart = false
            const map = { ...this.chooseDateList }
            const dateList = Object.keys(map)
            //const dateList = Object.values(map)
            const datesArray = []
            for (let i = 0; i < dateList.length; i++) {
                datesArray[i] = new Date(parseInt(dateList[i], 10))
                //datesArray[i] = dateList[i].date
            }
            // console.log(datesArray)
            this.$emit('date-selecteds', datesArray)
        },

        getCurDay() {
            var datetime = new Date();
            var year = datetime.getFullYear();
            var month = datetime.getMonth() + 1 < 10 ? "0" + (datetime.getMonth() + 1) : datetime.getMonth() + 1;
            var date = datetime.getDate() < 10 ? "0" + datetime.getDate() : datetime.getDate();
            return `${year}-${month}-${date}`
        },

        //得到当前年这个月分有多少天
        getDays(Y, M) {
            return new Date(Y, M, 0).getDate();
        },
        //得到当前年，这个月的一号是周几
        getWeek(Y, M) {
            let now = new Date()
            now.setFullYear(this.year)
            now.setMonth(this.month - 1, 1)
            now.setDate(1);
            return (now.getDay() + 6) % 7; // 将周日(0)转换为6，周一(1)转换为0，依此类推
        },
        /**
         * 获取本月日期
         */
        pushDays() {
            //将这个月多少天加入数组days
            const m = `${this.month > 9 ? this.month : '0' + this.month}`;
            for (let i = 1; i <= this.getDays(this.year, this.month); i++) {
                const d = `${i > 9 ? i : '0' + i}`,
                    date = `${this.year}-${m}-${d}`;
                this.days.push({
                    day: d,
                    isActive: false,
                    month: m,
                    year: `${this.year}`,
                    date,
                    timestamp: new Date(date).getTime(),//转换时间戳
                })
            }
            //获取上个月的日期
            this.getLastMonthDays()
            //获取下个月的日期
            this.getNextMonthDays()
        },
        /**
         * 获取下个月的日期
         */
        getNextMonthDays() {
            const m = this.month < 12 ? this.month + 1 : 1,
                y = this.month < 12 ? this.year : this.year + 1,
                // 修正计算逻辑：总天数=6周×7天=42天
                currentMonthDays = this.getDays(this.year, this.month),
                prevMonthDays = this.getWeek(this.year, this.month),
                // 动态计算需要补全的天数
                totalDays = prevMonthDays + currentMonthDays,
                remaining = totalDays % 7,
                len = remaining === 0 ? 0 : 7 - remaining,
                _m = `${m > 9 ? m : '0' + m}`;
            //将下个月要显示的天数加入days
            for (let i = 1; i <= len; i++) {
                const _d = `${i > 9 ? i : '0' + i}`,
                    date = `${y}-${_m}-${_d}`
                this.days.push({
                    day: _d,
                    month: _m,
                    year: `${y}`,
                    isActive: false,
                    isNextMonth: true,
                    date,
                    timestamp: new Date(date).getTime()
                })
            }
        },
        /**
         * 获取上个月的日期
         */
        getLastMonthDays() {
            const m = this.month > 1 ? this.month - 1 : this.year > 1970 ? 12 : 1,
                y = this.month > 1 ? this.year : this.year > 1970 ? this.year - 1 : 1970,
                len = this.getWeek(this.year, this.month),
                lastMonthDays = this.getDays(this.year, this.month - 1)
            // 调整为从周一开始显示
            for (let i = 0; i < len; i++) {
                const _m = `${m > 9 ? m : '0' + m}`,
                    date = `${y}-${_m}-${lastMonthDays - i}`
                this.days.unshift({
                    day: `${lastMonthDays - i}`,
                    month: _m,
                    year: `${y}`,
                    isActive: false,
                    isLastMonth: true,
                    date,
                    timestamp: new Date(date).getTime(),
                })
            }
        },
        /**
         * 上个月
         */
        handleShowLastMonth() {
            if (this.month > 1) {
                this.month = this.month - 1;
            } else if (this.year > 1970) {
                this.month = 12;
                this.year = this.year - 1;
            }
            this.dealDate();
        },
        /**
         * 下个月
         */
        handleShowNextMonth() {
            this.days = [];
            if (this.month < 12) {
                this.month = this.month + 1;
            } else {
                this.month = this.month = 1;
                this.year = this.year + 1;
            }
            this.dealDate();
        },
        /**
         * 当天
         */
        handleShowToday() {
            let now = new Date();
            this.year = now.getFullYear();
            this.month = now.getMonth() + 1;
            this.dealDate()
        },
        /**
         * 处理时间
         */
        dealDate() {
            this.days = [];
            const curDate = this.getCurDay()
            this.pushDays();
            // 判断 是否需要禁止选择某些时间段的时间
            if (this.restrictDate) {
                const keys = Object.keys(this.restrictDate);
                let day, timestamp;
                switch (keys[0]) {
                    case '1':
                        day = this.restrictDate[keys[0]] && this.restrictDate[keys[0]] || curDate;
                        timestamp = new Date(day).getTime();//转换时间戳
                        this.days.forEach(item => {
                            item.isCurToday = item.date === curDate
                            item.isPreviousDate = item.timestamp < timestamp
                        })
                        break;
                    case '2':
                        day = this.restrictDate[keys[0]] && this.restrictDate[keys[0]] || curDate;
                        timestamp = new Date(day).getTime();//转换时间戳
                        this.days.forEach(item => {
                            item.isCurToday = item.date === curDate
                            item.isPreviousDate = item.timestamp > timestamp
                        })
                        break;
                    case '3':
                        const s_d = this.restrictDate[keys[0]] && this.restrictDate[keys[0]][1] || curDate
                        const e_d = this.restrictDate[keys[0]] && this.restrictDate[keys[0]][2] || curDate
                        let s = new Date(s_d).getTime(),
                            e = new Date(e_d).getTime();
                        if (s > e) {
                            [s, e] = [e, s]
                        }
                        this.days.forEach(item => {
                            item.isCurToday = item.date === curDate
                            item.isPreviousDate = item.timestamp > s && item.timestamp < e
                        })
                        break
                    case '4':
                        const st_d = this.restrictDate[keys[0]] && this.restrictDate[keys[0]][1] || curDate
                        const en_d = this.restrictDate[keys[0]] && this.restrictDate[keys[0]][2] || curDate
                        let st = new Date(st_d).getTime(),
                            en = new Date(en_d).getTime();
                        if (st > en) {
                            [st, en] = [en, st]
                        }
                        this.days.forEach(item => {
                            item.isCurToday = item.date === curDate
                            item.isPreviousDate = item.timestamp < st || item.timestamp > en
                        })
                        break;
                    default:
                        this.days.forEach(item => {
                            item.isCurToday = item.date === curDate
                        })
                }
            } else {
                this.days.forEach(item => {
                    item.isCurToday = item.date === curDate
                })
            }
            this.getActiveDay()
        },
        /**
         * 清空选择
         */
        handleClear() {
            this.isChooseOne = false;
            this.startTime = null;
            this.endTime = null;
            this.chooseDateList = {};
            this.days.forEach(item => {
                item.isActive = false
            })
        },

        /**
         * 选择时间
         * @param time
         */
        handleChooseDay(time = {}) {
            // 判断 是否是禁止选择的日期 是否是
            if (this.restrictDate && time.isPreviousDate) {
                return
            }
            //是否是多选
            if (this.isMultipleChoice) {
                this.chooseDateList = {}
                //选择开始时间-结束时间
                if (this.isChooseOne) {
                    this.endTime = time;
                    const { timestamp } = this.startTime || {};
                    //如果 选择的开始日期大于结束日期 则调换开始日期与结束日期
                    if (timestamp > time.timestamp) {
                        [this.startTime, this.endTime] = [this.endTime, this.startTime]
                    }
                    this.getActiveDay();
                    this.isChooseOne = false
                    this.$emit('chooseDays', this.chooseDateList)
                } else {
                    this.isChooseOne = true
                    this.startTime = time;
                    //给选择的时间范围选中
                    this.days.forEach(item => {
                        item.isActive = item.timestamp === this.startTime.timestamp;
                    })
                }
            } else {
                this.days.forEach(item => {
                    if (item.timestamp === time.timestamp) {
                        item.isActive = !time.isActive;
                        if (time.isActive) {
                            const { date, day, month, timestamp, year } = item
                            this.chooseDateList[time.timestamp] = {
                                date,
                                day,
                                month,
                                timestamp,
                                year
                            }
                        } else {
                            delete this.chooseDateList[time.timestamp]
                        }
                    }
                })

                this.$emit('chooseDays', this.chooseDateList)
            }

        },
        /**
         * 给选择的日期范围加上选中状态
         */
        getActiveDay() {
            if (this.isMultipleChoice) {
                if (!this.startTime || !this.endTime) {
                    return
                }
                //给选择的时间范围选中
                const { timestamp } = this.startTime || {};
                const { timestamp: endTimestamp } = this.endTime || {};
                this.days.forEach(item => {
                    item.isActive = item.timestamp >= timestamp && item.timestamp <= endTimestamp && !item.isPreviousDate;
                    //是否显示点
                    item.isShowPoint = this.pointMap[item.timestamp]
                    if (item.timestamp >= timestamp && item.timestamp <= endTimestamp && !item.isPreviousDate) {
                        const { date, day, month, timestamp, year } = item
                        this.chooseDateList[item.timestamp] = {
                            date,
                            day,
                            month,
                            timestamp,
                            year
                        }
                    } else {
                        delete this.chooseDateList[item.timestamp];
                    }
                })
            } else {
                this.days.forEach(item => {
                    //是否显示点
                    item.isShowPoint = this.pointMap[item.timestamp]
                    //已选择的数据加上状态
                    item.isActive = !!this.chooseDateList[item.timestamp]
                })
            }
        }
    },
    mounted() {
        this.handleShowToday()
    }
}
</script>
<style>
ul {
    list-style: none;
}

.w-calender-container {
    width: 90%;
    min-width: 400px;
    border: 1px solid #ddd;
    padding: 5px;
    box-sizing: border-box;
}

.w-top {
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.1% 0;
    border-bottom: 1px solid #ddd;
    box-sizing: border-box;
}

.w-top .w-top-date {
    white-space: nowrap;
    font-size: 18px;
    font-weight: bold;
}

.w-top .w-btn-wrap {
    min-width: 200px;
    display: flex;
    justify-content: space-around;
    color: #409eff;
}

.w-btn-wrap span {
    flex: 1;
    cursor: pointer;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 16px;
}

.w-btn-wrap span:last-child {
    color: #ff6200;
}

.w-date_wrap {
    width: 100%;
    height: auto;
}

.w-date_wrap .w-week {
    width: 100%;
    display: flex;
    flex-direction: row;
    padding: 5px 5px 5px 5px;
    font-size: 16px;
    box-sizing: border-box;
}

.w-date_wrap .w-week li {
    width: calc(100% / 7); /* 确保7等分 */
    height: 10px;
    padding: 0 0 0 30px;
}

.w-date_wrap .w-day {
    width: 100%;
    display: flex;
    flex-direction: row;
    padding: 0 20px;
    font-size: 16px;
    flex-wrap: wrap;
    box-sizing: border-box;
}

.w-day li {
    position: relative;
    cursor: pointer;
    width: 14.28%;
    padding: 4%;
    border: 1px solid #ddd;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    box-sizing: border-box;
}

.w-day li .w-point {
    position: absolute;
    bottom: 26%;
    background: #ff0000;
    display: flex;
    width: 4px;
    height: 4px;
    border-radius: 50%;
}

.w-day li:nth-child(n + 8) {
    border-top: none;
}

.w-day li:nth-child(n + 1) {
    border-right: none;
}

.w-day li:nth-child(7n) {
    border-right: 1px solid #ddd;
}

.date-tip {
    height: 40px;
    width: 100%;
    display: flex;
}

.tip_row {
    width: 100px;
    height: 100%;
    display: flex;
    align-items: center;
}

.tip_row:first-child {
    padding-left: 20px;
    box-sizing: border-box;
}

.tip_row .square {
    width: 20px;
    height: 20px;
    display: flex;
    background: #fff;
    border: 1px solid #c0c4cc;
}

.tip_row .chosen {
    background: #409eff;
    border: 0;
}

.tip_row .title {
    display: flex;
    padding-left: 6px;
    box-sizing: border-box;
    white-space: nowrap;
}

.c-isCurMonth {
    background: #fff;
    color: #c0c4cc;
}

.c-isCurToday {
    background: #fff;
    color: #409eff;
}

.c-isActive {
    background: #409eff;
    color: #f2f8fe;
}

.chooseMonth {
    width: 120px;
}

.isCurYearDay {
    cursor: not-allowed !important;
    opacity: 0.6;
}

.c-isCurMonth.c-isActive {
    background: rgba(64, 158, 255, 0.56);
}

.c-is-previous-date {
    background: rgba(192, 196, 204, 0.2);
    color: #c0c4cc;
    cursor: not-allowed !important;
}

.user-select-none {
    -webkit-user-select: none; /* Chrome, Safari, Opera */
    -moz-user-select: none; /* Firefox */
    -ms-user-select: none; /* Internet Explorer/Edge */
    user-select: none; /* Non-prefixed version, currently supported by all major browsers */
}
</style>
